AnimatedWidget

记得不记得在 Android 中 View 有一个方法 animate()?它免去了我们写 ValueAnimator、获取值、在更新 UI 等一系列操作,直接调用 View 的 animate 方法即可,简化了操作。

在之前的例子中,我们需要为 AnimationController 添加监听器,然后再里面在调用 setState 来更新 UI,在 Flutter 中,也有个帮助我们简化动画操作的 Widget —— AnimatedWidget,它的使用方法也十分简单(这不废话么,使用复杂还不如直接使用 AnimationController):

  1. 创建 AnimatedWidget 子类
  2. 其构造方法需要一个 Listenable 对象参数
  3. 在 build 方法中返回需要执行动画的内容,其属性直接使用 Listenable 对象获取
  4. 在 State 的 build 方法中,直接 return 第一步创建的对象即可。

Listenable 是什么呢?它是一个抽象类,Animation 是它的实现类,这下明白了吧

所以之前的例子就可以这样写:

class AnimationRouteState extends State<AnimationRoute>
    with TickerProviderStateMixin {
  Animation<Color> animation;
  AnimationController controller;

  initState() {
    super.initState();
    controller = new AnimationController(
        duration: const Duration(seconds: 3), vsync: this);
    animation = new ColorTween(begin: Colors.lightGreenAccent, end: Colors.red)
        .animate(controller);
    controller.addListener((){
      if(controller.isCompleted){
        controller.repeat(reverse: true);
      }
    });
    //启动动画
    controller.forward();
  }

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return AnimatedColor(
      animation: animation,
    );
  }
}

class AnimatedColor extends AnimatedWidget {
  AnimatedColor({Key key, Animation<Color> animation})
      : super(key: key, listenable: animation);

  @override
  Widget build(BuildContext context) {
    Animation<Color> animation = listenable;
    return Container(
      color: animation.value,
      alignment: Alignment.center,
      width: 400,
      height: 400,
    );
  }
}

效果如下:

总结一下,AnimatedWidget 其实相当于是 Animation 的一个包装类,省去了我们手动 setState。

AnimatedBuilder

在上面的例子中,AnimationRouteStatebuild 方法直接返回了 AnimatedColor 对象,试想一下这种情况,我们的布局是一个 Container 包含了一个 Text,我们想要动态改变 Container 的背景色,按照 AnimatedWidget 的方法就是我们每次更新背景色都需要连同 Text 一起重新绘制,有一种牵一发而动全身的感觉,这样就比较耗费资源,使用 AnimatedBuilder 可以避免这种过度绘制。

AnimatedBuilder 的使用方法也很简单,它有三个参数:

  • animation,必填参数,作用于控件上的动画
  • builder,必填参数,传入一个 build 方法自定义动画起作用的控件
  • child,该参数不是必填参数,如果不为 null,则该控件可以作为子控件添加到 builder 返回的 Widget 中,但它只绘制一次

看例子:

class AnimationRouteState extends State<AnimationRoute>
    with TickerProviderStateMixin {
  Animation<Color> animation;
  AnimationController controller;

  initState() {
    super.initState();
    controller = new AnimationController(
        duration: const Duration(seconds: 1), vsync: this);
    animation = new ColorTween(begin: Colors.lightGreenAccent, end: Colors.red)
        .animate(controller);
    controller.addListener(() {
      if (controller.isCompleted) {
        controller.repeat(reverse: true);
      }
    });
    //启动动画
    controller.forward();
  }

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      appBar: AppBar(
        title: Text(""),
      ),
      body: AnimatedBuilder(
        animation: animation,
        builder: (BuildContext context,Widget child){
          return Container(
            width: 400,
            height: 400,
            alignment: Alignment.center,
            color: animation.value,
              //这个 child 只会绘制一次
            child: child,
          );
        },
        child: Text("这个文本不会重复绘制",style:TextStyle(fontSize: 50)),
      ),
    );
  }
}

其效果是这样的:

results matching ""

    No results matching ""